﻿using System;
using System.Collections.Generic;
using System.Linq;
using Citi.HeadCount.Domain.NHibernate;
using NHibernate;
using NHibernate.Linq;
using System.Linq.Expressions;

namespace Citi.HeadCount.Domain.Repository
{
    public class RepositoryBase<T> : IRepository<T> where T : EntityBase
    {
        private const string ActiveField = "Active";
        private const string CodeField = "Code";
        private const string DescriptionField = "Description";

        private static ISession session;

        protected static ISession Session
        {
            get
            {
                if (session == null || !session.IsOpen)
                {
                    session = new SessionFactory().CreateSessionFactory().OpenSession();
                    //session.FlushMode = FlushMode.Always;
                    return session;
                }

                return session;
            }
        }

        public void Save(T entity)
        {
            try
            {
                Session.SaveOrUpdate(entity);
                Session.Flush();
                Session.Refresh(entity);
            }
            catch (Exception ex)
            {
                Session.Refresh(entity);
            }
        }

        public void Save(IEnumerable<T> entities)
        {
            try
            {
                using (var transaction = Session.BeginTransaction())
                {
                    try
                    {
                        Session.Flush();
                    }
                    catch { }

                    foreach (var entity in entities)
                    {
                        Session.SaveOrUpdate(entity);
                        Session.Refresh(entity);
                    }

                    Session.Flush();

                    transaction.Commit();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void Delete(T entity)
        {
            try
            {
                Session.Delete(entity);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void ExecuteQuery(IEnumerable<string> clause)
        {
            try
            {
                using (ISession session = Session)
                using (var transaction = session.BeginTransaction())
                {
                    foreach (var item in clause)
                    {
                        var query = session.CreateQuery(item).ExecuteUpdate();
                    }

                    transaction.Commit();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void DeleteByReferenceDate(DateTime referenceDate)
        {
            try
            {
                using (ISession session = Session)
                using (var transaction = session.BeginTransaction())
                {
                    var entities = GetAll().Where(x => x.ReferenceDate == referenceDate);

                    //session.SetBatchSize(1000);
                    entities.ForEach(x => session.Delete(x));

                    transaction.Commit();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public T GetById(int id)
        {
            return Session.Get<T>(id);
        }

        public void Inative(int id)
        {
            var entity = GetById(id);

            var propertyActive = typeof(T).GetProperty(ActiveField);

            if (propertyActive == null)
                throw new ArgumentException(string.Format("This entity does not contains a {0} Field", ActiveField));

            propertyActive.SetValue(entity, false, null);
            Save(entity);
        }

        public List<T> GetAllDistinctByCodeOrDescription()
        {
            var propertyInfo = typeof(T).GetProperty(CodeField);

            if (propertyInfo == null)
                propertyInfo = typeof(T).GetProperty(DescriptionField);

            return Session.Query<T>().ToList();
        }

        public T GetByDescription(string description)
        {
            var propertyInfo = typeof(T).GetProperty(DescriptionField);

            if (propertyInfo == null) throw new ArgumentException(string.Format("This entity does not contains a {0} Field", DescriptionField)); ;

            return Session.Query<T>().ToList().FirstOrDefault(x => propertyInfo.GetValue(x, null).ToString() == description);
        }

        public T GetByCode(string code)
        {
            var propertyInfo = typeof(T).GetProperty(CodeField);

            if (propertyInfo == null) throw new ArgumentException(string.Format("This entity does not contains a {0} Field", CodeField)); ;

            return Session.Query<T>().ToList().FirstOrDefault(x => propertyInfo.GetValue(x, null).ToString() == code);
        }

        private T GetProperty(object entity, string propertyName)
        {
            return (T)Convert.ChangeType(entity.GetType().GetProperty(propertyName).GetValue(entity, null), typeof(T));
        }

        public IQueryable<T> GetAll()
        {
            return Session.Query<T>();
        }
    }
}
